package ru.autofan.filters;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.springframework.web.util.UrlPathHelper;

/**
 * Filter for rewrite URLs to format. Idea from
 * com.caucho.servlets.RewriteFilter.
 * 
 * @author egor
 */
public class RewriteUrlsFilter implements Filter {

	/** Excludes from rewriting templates (Regex) */
	private Set<String> excludesPaths = new HashSet<String>();

	/** Map for rewrite paths: key - pattern(Regex), value - target */
	private Map<String, String> rewriteRules;

	private ServletContext servletContext;

	private UrlPathHelper urlPathHelper;

	public void destroy() {
		// TODO Auto-generated method stub
	}

	public RewriteUrlsFilter() {
		urlPathHelper = new UrlPathHelper();
		excludesPaths = new HashSet<String>();
	}

	protected boolean isExcluded(String path) {
		for (Iterator<String> it = this.excludesPaths.iterator(); it.hasNext();) {
			String registeredPath = it.next();
			Pattern p = Pattern.compile(registeredPath);
			if (p.matcher(path).find()) {
				return true;
			}
		}
		return false;
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {

		String requestPath = urlPathHelper
				.getPathWithinApplication((HttpServletRequest) request);
		// Checks pattern match(using Regex) for excludes paths
		if (!isExcluded(requestPath)) {
			String replacement = rewritePath(requestPath);
			if (replacement != null) {
				servletContext.getRequestDispatcher(replacement).forward(
						request, response);
				return;
			}
		}
		chain.doFilter(request, response);
		// Nothing match
	}

	public Set<String> getExcludesPaths() {
		return excludesPaths;
	}

	public Map<String, String> getRewriteRules() {
		return rewriteRules;
	}

	public void init(FilterConfig filterConfig) {

		servletContext = filterConfig.getServletContext();
		String path = servletContext.getRealPath("")
				+ "/WEB-INF/classes/rewrite-rules.properties";
		try {
			generateRules(new FileInputStream(path));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}

	private String replace(Matcher matcher, String target) {

		Pattern p = Pattern.compile("\\$[1-9]");
		Matcher m = p.matcher(target);
		StringBuffer sb = new StringBuffer();

		while (m.find()) {
			String param = m.group();
			int index = Integer.parseInt(param.substring(1));
			m.appendReplacement(sb, matcher.group(index));
		}
		m.appendTail(sb);
		return sb.toString();

	}

	/**
	 * Rewrite path within application according regex rules.
	 * 
	 * @param lookupPath -
	 *            path within application.
	 * @return result path, or null - if not find matches.
	 */
	public String rewritePath(String lookupPath) {

		String result = null;

		// Rewriting URL
		for (Iterator<String> it = this.rewriteRules.keySet().iterator(); it
				.hasNext();) {
			String registeredPath = it.next();
			Pattern p = Pattern.compile(registeredPath);
			Matcher matcher = p.matcher(lookupPath);
			if (matcher.find()) {
				result = replace(matcher, rewriteRules.get(registeredPath));
				break;
			}
		}

		return result;
	}

	public void setExcludesPaths(Set<String> excludesPaths) {
		this.excludesPaths = excludesPaths;
	}

	public void generateRules(InputStream stream) {

		InputStreamReader reader = new InputStreamReader(stream);
		BufferedReader bufferdReader = new BufferedReader(reader);
		String string;
		LinkedHashMap<String, String> pairs = new LinkedHashMap<String, String>();
		try {
			while ((string = bufferdReader.readLine()) != null) {
				if (string.trim().length() > 0 && !string.startsWith("#")) {
					int indexOfEq = string.indexOf("=");
					String key = string.substring(0, indexOfEq);
					String value = string.substring(indexOfEq + 1, string
							.length());
					pairs.put(key, value);
				}

			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		this.rewriteRules = pairs;
	}
}
